//Package models 模型
package models

import (
	"os"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/astaxie/beego"

	"github.com/Unknwon/com"
	"github.com/astaxie/beego/orm"
	_ "github.com/mattn/go-sqlite3" //包前面的_符号标记，导入该包时，调用初始化函数init
)

const (
	_DB_NAME        = "data/beeblog.db"
	_SQLITE3_DRIVER = "sqlite3"
)

//Category 分类
type Category struct {
	Id              int64
	Title           string    //255
	Created         time.Time `orm:"index"` //对字段做索引
	Views           int64     `orm:"index"`
	TopicTime       time.Time `orm:"index"`
	TopicCount      int64
	TopicLastUserId int64
}

// Topic 文章
type Topic struct {
	Id              int64
	Uid             int64
	Title           string
	Category        string //分类
	Labels          string
	Content         string `orm:"size(5000)"` //orm:"size(5000)"设置表字段的大小为5000
	Attachment      string
	Created         time.Time `orm:"index"` //对字段做索引
	Updated         time.Time `orm:"index"` //对字段做索引
	Views           int64     `orm:"index"`
	Author          string
	ReplyTime       time.Time
	ReplyCount      int64
	ReplyLastUserId int64
}

// Comment 评论
type Comment struct {
	Id      int64
	Tid     int64
	Name    string
	Content string    `orm:"size(1000)"`
	Created time.Time `orm:"index"`
}

// RegisterDB 注册数据库
func RegisterDB() {
	if !com.IsExist(_DB_NAME) {
		os.MkdirAll(path.Dir(_DB_NAME), os.ModePerm)
		os.Create(_DB_NAME)
	}
	//注册模型
	orm.RegisterModel(new(Category), new(Topic), new(Comment)) //new是内建函数，用来新建并用零值初始化对象
	//注册驱动
	orm.RegisterDriver(_SQLITE3_DRIVER, orm.DRSqlite)
	//注册数据库
	orm.RegisterDataBase("default", _SQLITE3_DRIVER, _DB_NAME, 10)
}

// AddCategory 添加分类
func AddCategory(name string) error {
	cate := &Category{Title: name}
	o := orm.NewOrm()
	qs := o.QueryTable("category")
	if err := qs.Filter("title", name).One(cate); err == nil {
		return err
	}
	_, err := o.Insert(cate)
	return err
}

// GetAllCategories 获取所有分类
func GetAllCategories() ([]*Category, error) {
	o := orm.NewOrm()
	cates := make([]*Category, 0)
	if _, err := o.QueryTable("category").All(&cates); err != nil {
		return cates, err
	}
	return cates, nil
}

//ModifyCategoryTopicCount 更新分类统计
func ModifyCategoryTopicCount(title string, topicCountAdd int64) error {
	if len(title) == 0 {
		return nil
	}
	o := orm.NewOrm()
	cate := &Category{Title: title}
	if err := o.Read(cate, "Title"); err != nil {
		beego.Error(err)
		return nil
	}
	cate.TopicCount = cate.TopicCount + topicCountAdd
	if cate.TopicCount < 0 {
		cate.TopicCount = 0
	}
	_, err := o.Update(cate)
	return err
}

// DelCategory 删除分类
func DelCategory(id string) error {
	idN, err := strconv.ParseInt(id, 10, 64)
	if err != nil {
		return err
	}
	if _, err := orm.NewOrm().Delete(&Category{Id: idN}); err != nil {
		return err
	}
	return nil
}

// AddTopic 添加文章
func AddTopic(title, category, labels, content, attachment string) error {
	labels = serializeLabels(labels)
	o := orm.NewOrm()
	if _, err := o.Insert(&Topic{Title: title, Category: category, Labels: labels, Content: content, Attachment: attachment, Created: time.Now(), Updated: time.Now()}); err != nil {
		return err
	}

	//更新分类统计
	err := ModifyCategoryTopicCount(category, 1)
	if err != nil {
		beego.Error(err)
	}
	return nil
}

// GetAllTpics 获取所有文章
func GetAllTpics(cate, label string, isDesc bool) ([]*Topic, error) {
	topics := []*Topic{}
	o := orm.NewOrm()
	var err error
	qs := o.QueryTable("topic")
	if len(cate) > 0 {
		qs = qs.Filter("category", cate) //非一行链式操作的返回值需要重新赋值给变量qs
	}
	if len(label) > 0 {
		qs = qs.Filter("labels__contains", "$"+label+"#")
	}

	if isDesc {
		/*
			带查询条件时，只能一句连续查询，分开多句查询查询调件会丢失
			原因：源码中的方法是值方法而非引用方法，带查询参数的对象在返回值中
			//github.com\astaxie\beego\orm\orm_queryset.go
			func (o querySet) OrderBy(exprs ...string) QuerySeter {
				o.orders = exprs
				return &o
			}
		*/
		_, err = qs.OrderBy("-created").All(&topics)
	} else {
		_, err = qs.All(&topics)
	}
	if err != nil {
		return topics, err
	}
	for _, topic := range topics {
		topic.Labels = deserializeLabels(topic.Labels)
	}
	return topics, nil
}

func serializeLabels(labels string) string {
	if len(labels) > 0 {
		labels = "$" + strings.Join(strings.Split(labels, " "), "#$") + "#"
	}
	return labels
}
func deserializeLabels(labels string) string {
	if len(labels) > 0 {
		labels = strings.Replace(labels, "#$", " ", -1)
		labels = labels[1 : len(labels)-1]
		// labels = strings.Replace(strings.Replace(labels, "#", " ", -1), "$", "", -1)//多重替换，去掉$和#
	}
	return labels
}

// GetTopic 获取文章
func GetTopic(tid string) (*Topic, error) {
	tidNum, err := strconv.ParseInt(tid, 10, 64)
	if err != nil {
		return nil, err
	}

	o := orm.NewOrm()
	topic := &Topic{}
	if err := o.QueryTable("topic").Filter("id", tidNum).One(topic); err != nil {
		return topic, err
	}
	topic.Views++
	if _, err = o.Update(topic); err != nil {
		return topic, err
	}

	topic.Labels = deserializeLabels(topic.Labels)
	return topic, nil
}

// ModifyTopic 修改文章
func ModifyTopic(tid, title, category, label, content, attachment string) error {
	tidNum, err := strconv.ParseInt(tid, 10, 64)
	if err != nil {
		return err
	}

	o := orm.NewOrm()
	topic := &Topic{Id: tidNum}
	if err = o.Read(topic); err != nil {
		return err
	}

	oldCate := topic.Category
	oldAttachment := topic.Attachment
	topic.Title = title
	topic.Category = category
	topic.Labels = serializeLabels(label)
	topic.Content = content
	topic.Attachment = attachment
	topic.Updated = time.Now()
	_, err = o.Update(topic)
	if err != nil {
		return err
	}
	if oldCate != category {
		//更新分类统计信息
		ModifyCategoryTopicCount(category, 1)
		ModifyCategoryTopicCount(oldCate, -1)
	}
	//删除旧的附件
	if len(oldAttachment) > 0 && oldAttachment != attachment {
		attachmentPath := path.Join("attachment", oldAttachment)
		if com.IsExist(attachmentPath) {
			os.Remove(attachmentPath)
		}
	}
	return nil
}

// DelTopic 删除文章
func DelTopic(tid string) error {
	idN, err := strconv.ParseInt(tid, 10, 64)
	if err != nil {
		return err
	}
	topic, err1 := GetTopic(tid)
	if err1 != nil {
		return nil
	}
	_, err = orm.NewOrm().Delete(&Topic{Id: idN})
	if err != nil {
		return err
	}
	//更新分类统计
	err = ModifyCategoryTopicCount(topic.Category, -1)
	if err != nil {
		beego.Error(err)
	}

	return nil
}

// AddReply 添加回复
func AddReply(tid, nickname, content string) error {
	tidNum, err := strconv.ParseInt(tid, 10, 64)
	if err != nil {
		return err
	}
	reply := &Comment{Tid: tidNum, Name: nickname, Content: content, Created: time.Now()}
	_, err = orm.NewOrm().Insert(reply)
	if err != nil {
		return err
	}
	if err = ModifyTopicReplyCount(tidNum, 1); err != nil {
		beego.Error(err)
	}
	return nil
}

// GetAllReplies 获取所有回复
func GetAllReplies(tid string) ([]*Comment, error) {
	tidNum, err := strconv.ParseInt(tid, 10, 64)
	comments := []*Comment{}
	if err != nil {
		return comments, nil
	}
	_, err = orm.NewOrm().QueryTable("comment").Filter("tid", tidNum).OrderBy("-created").All(&comments)
	return comments, err
}

// DelReply 删除回复
func DelReply(rid string) error {
	ridNum, err := strconv.ParseInt(rid, 10, 64)
	if err != nil {
		return err
	}
	o := orm.NewOrm()
	comment := &Comment{Id: ridNum}
	if err = o.Read(comment); err != nil {
		beego.Error(err)
		return nil
	}
	_, err = o.Delete(&Comment{Id: ridNum})
	if err != nil {
		return err
	}
	if err := ModifyTopicReplyCount(comment.Tid, -1); err != nil {
		beego.Error(err)
	}
	return nil
}

// ModifyTopicReplyCount 修改文章回复数
func ModifyTopicReplyCount(tid int64, replyCountAdd int64) error {
	o := orm.NewOrm()
	topic := &Topic{Id: tid}
	if err := o.Read(topic); err != nil {
		beego.Error(err)
		return nil
	}
	topic.ReplyCount = topic.ReplyCount + replyCountAdd
	if topic.ReplyCount < 0 {
		topic.ReplyCount = 0
	}
	if replyCountAdd > 0 {
		topic.ReplyTime = time.Now() //更新最后回复时间
	} else {
		comment := new(Comment)
		if err := o.QueryTable("comment").OrderBy("-created").Limit(1).One(comment); err != nil {
			beego.Error(err)
			return nil
		}
		topic.ReplyTime = comment.Created
	}
	_, err := o.Update(topic)
	return err
}
